using System;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using Microsoft.SharePoint;

namespace DE.Sdc.SharePoint.Common.Deployment
{
    [Serializable]
    [XmlType(AnonymousType = true, Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure")]
    [XmlRoot(Namespace = "http://sdc/2009/07/Deployment/1.0/ContentStructure", IsNullable = false)]
    public class Web : Folder
    {
        public Web()
        {
            Webs = new HierarchicalList<Folder, Folder>(this);
            Lists = new HierarchicalList<Folder, Folder>(this);
        }

        /// <remarks/>
        [XmlAttribute]
        public string Description { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Template { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public uint Lcid { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string Theme { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string MasterUrl { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public string CustomMasterUrl { get; set; }
        
        /// <remarks/>
        [XmlAttribute]
        public bool InheritMaster { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool InheritCustomMaster { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool ParserEnabled { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public bool NoCrawl { get; set; }

        /// <remarks/>
        [XmlAttribute]
        public WebASPXPageIndexMode AspxPageIndexMode { get; set; }

        /// <remarks/>
        [XmlArrayItem("Web", typeof(Web))]
        public HierarchicalList<Folder, Folder> Webs { get; set; }

        /// <remarks/>
        [XmlArrayItem("List", typeof(List))]
        public HierarchicalList<Folder, Folder> Lists { get; set; }

        [XmlElement]
        public Navigation Navigation { get; set; }

        /// <summary>
        /// Adds, Replaces, Deletes, Modifies or Moves a web in a given parent web
        /// </summary>
        /// <param name="webDefinition">Xml object for a web</param>
        /// <param name="web">A web based on the webDefinition or null</param>
        /// <param name="parentWeb">The parent web for the current import web</param>
        protected internal static void ImportWeb(Web webDefinition, SPWeb web, SPWeb parentWeb)
        {
            Trace.WriteLine("Try to import web '" + webDefinition.Title + "' with url '" + webDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ImportWeb");

            if (web == null)
            {
                if (webDefinition.Action == ActionType.Add || webDefinition.Action == ActionType.Replace)
                    web = CreateWeb(webDefinition, parentWeb);
                else
                    return;
            }
            else if (webDefinition.Action == ActionType.Replace)
            {
                DeleteWeb(web);
                web = CreateWeb(webDefinition, parentWeb);
            }
            else if (webDefinition.Action == ActionType.Delete)
            {
                DeleteWeb(web);
                return;
            }

            if (webDefinition.Action != ActionType.None &&
                webDefinition.Action <= ActionType.Modify)
                ModifyWeb(webDefinition, web);


            for (int index = 0; index < webDefinition.Lists.Count; index++)
            {
                List listDefinition = (List)webDefinition.Lists[index];
                SPList list = null;

                try
                {
                    string listName = NamedElement.ResolveResource(listDefinition.Title, web.Locale);
                    string listUrl = GetRootedUrl(NamedElement.ResolveResource(listDefinition.Url, web.Locale),
                                                  web.ServerRelativeUrl);

                    list = !string.IsNullOrEmpty(listUrl)
                               ? web.GetList(listUrl)
                               : web.Lists[listName];
                }
                catch (FileNotFoundException) { }
                catch (ArgumentNullException) { }
                catch (ArgumentException) { }

                List.ImportList(listDefinition, list, web);
            }

            for (int index = 0; index < webDefinition.Webs.Count; index++)
            {
                Web subWebDefinition = (Web)webDefinition.Webs[index];
                string subWebTitle = NamedElement.ResolveResource(subWebDefinition.Title, web.Locale);
                string subWebUrl = GetRootedUrl(NamedElement.ResolveResource(subWebDefinition.Url, web.Locale),
                                              web.ServerRelativeUrl);

                SPWeb subWeb = !string.IsNullOrEmpty(subWebUrl)
                               ? web.Site.OpenWeb(subWebUrl)
                               : web.Webs[subWebTitle];
                ImportWeb(subWebDefinition, subWeb.Exists ? subWeb : null, web);
            }

            if (webDefinition.Navigation != null)
            {
                using (SPSite navigationSite = new SPSite(web.Site.ID))
                {
                    using (SPWeb navigationWeb = navigationSite.OpenWeb(web.ID))
                    {
                        Navigation.ModifyNavigation(webDefinition.Navigation, navigationWeb.Navigation);
                    }
                }
            }

            Permissions.SetPermissions(webDefinition, web, web);
            web.Dispose();

            Trace.WriteLine("Web '" + webDefinition.Title + "' with url '" + webDefinition.AbsoluteUrl + "' imported.", "ContentStructureImporter:ImportWeb");
        }

        /// <summary>
        /// Creates a web in a given web
        /// </summary>
        /// <param name="webDefinition">Xml object for a web</param>
        /// <param name="parentWeb">The parent web where to create the web</param>
        /// <returns>The created web</returns>
        private static SPWeb CreateWeb(Web webDefinition, SPWeb parentWeb)
        {
            string webName = NamedElement.ResolveResource(webDefinition.Title, parentWeb.Locale);
            string webUrl = NamedElement.ResolveResource(webDefinition.Url, parentWeb.Locale);
            string webDescription = NamedElement.ResolveResource(webDefinition.Description, parentWeb.Locale);
            string webTemplate = NamedElement.ResolveResource(webDefinition.Template, parentWeb.Locale);
            Trace.WriteLine("Try to create web '" + webDefinition.Title + "' with url '" + webDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:CreateWeb");

            SPWeb newWeb = parentWeb.Site.AllWebs.Add(webDefinition.AbsoluteUrl,
                                                      webUrl ?? webName,
                                                      webDescription,
                                                      webDefinition.Lcid, webTemplate, false, false);

            newWeb.Update();
            Trace.WriteLine("Web '" + webDefinition.Title + "' with url '" + webDefinition.AbsoluteUrl + "' created.", "ContentStructureImporter:CreateWeb");
            return newWeb;
        }

        /// <summary>
        /// Changes/Updates the properties of the given web
        /// </summary>
        /// <param name="webDefinition">Xml object for a web</param>
        /// <param name="web">A web based on the webDefinition</param>
        private static void ModifyWeb(Web webDefinition, SPWeb web)
        {
            Trace.WriteLine("Try to modify web '" + webDefinition.Title + "' with url '" + webDefinition.AbsoluteUrl + "'.", "ContentStructureImporter:ModifyWeb");

            if (webDefinition.InheritMaster)
                web.MasterUrl = web.ParentWeb != null ? web.ParentWeb.MasterUrl : web.MasterUrl;
            else if (!string.IsNullOrEmpty(webDefinition.MasterUrl))
                web.MasterUrl = NamedElement.ResolveResource(webDefinition.MasterUrl, web.Locale);

            if (webDefinition.InheritCustomMaster)
                web.CustomMasterUrl = web.ParentWeb != null ? web.ParentWeb.CustomMasterUrl : web.CustomMasterUrl;
            else if (!string.IsNullOrEmpty(webDefinition.CustomMasterUrl))
                web.CustomMasterUrl = NamedElement.ResolveResource(webDefinition.CustomMasterUrl, web.Locale);

            ModifyProperty("__InheritsMasterUrl", webDefinition.InheritMaster.ToString(), false,
                               web.AllProperties);
            ModifyProperty("__InheritsCustomMasterUrl", webDefinition.InheritCustomMaster.ToString(), false,
                               web.AllProperties);

            web.NoCrawl = webDefinition.NoCrawl;
            web.ASPXPageIndexMode = webDefinition.AspxPageIndexMode;

            if (!string.IsNullOrEmpty(webDefinition.Title))
                web.Title = NamedElement.ResolveResource(webDefinition.Title, web.Locale);

            web.ParserEnabled = webDefinition.ParserEnabled;
            web.Update();

            if (!string.IsNullOrEmpty(webDefinition.Theme))
            {
                using (SPWeb themeWeb = web.Site.OpenWeb(web.ServerRelativeUrl))
                {
                    themeWeb.AllowUnsafeUpdates = true;
                    themeWeb.ApplyTheme(NamedElement.ResolveResource(webDefinition.Theme, themeWeb.Locale));
                    themeWeb.Update();
                    themeWeb.AllowUnsafeUpdates = false;
                }
            }

            Trace.WriteLine("Web '" + webDefinition.Title + "' with url '" + webDefinition.AbsoluteUrl + "' modified.", "ContentStructureImporter:ModifyWeb");
        }

        /// <summary>
        /// Deletes a web and all subwebs
        /// </summary>
        /// <param name="web">The web to delete</param>
        private static void DeleteWeb(SPWeb web)
        {
            Trace.WriteLine("Try to delete web '" + web.Title + "' with url '" + web.Url + "'.", "ContentStructureImporter:DeleteWeb");

            if (web.Webs.Count > 0)
            {
                for (int index = 0; index < web.Webs.Count; index++)
                {
                    DeleteWeb(web.Webs[index]);
                }
            }

            web.Delete();
            Trace.WriteLine("Web '" + web.Title + "' with url '" + web.Url + "' deleted.", "ContentStructureImporter:DeleteWeb");
        }
    }
    
}